package day24.map;

import java.util.*;

/**
 * Hashtable功能测试类
 * 演示Hashtable的基本操作和特性
 * Hashtable与HashMap类似，但它是线程安全的，并且不支持null键和null值
 * 
 * Hashtable的特点：
 * 1. 线程安全：所有方法都是同步的，可以在多线程环境中安全使用
 * 2. 不允许null键和null值：如果尝试插入null会抛出NullPointerException
 * 3. 继承自Dictionary类：这是Hashtable特有的，而HashMap继承自AbstractMap
 * 4. 使用Enumeration进行遍历：这是Hashtable特有的遍历方式
 * 5. 性能相对较低：由于同步机制，性能不如HashMap
 */
public class HashtableTest {
    public static void main(String[] args) {
        // 1. Hashtable基本操作测试
        System.out.println("======= Hashtable基本操作测试 =======");
        // 创建一个Hashtable实例，键为String类型，值为String类型
        // Hashtable的初始容量为11，负载因子为0.75
        Hashtable<String, String> hashtable = new Hashtable<>();
        
        // 添加键值对 - put方法将指定的键值对添加到Hashtable中
        // put方法是同步的，确保线程安全
        hashtable.put("name", "张三");     // 添加姓名键值对
        hashtable.put("age", "25");       // 添加年龄键值对
        hashtable.put("city", "北京");     // 添加城市键值对
        hashtable.put("job", "程序员");    // 添加职业键值对
        
        // 打印整个Hashtable的内容，toString方法会显示所有键值对
        System.out.println("Hashtable内容: " + hashtable);
        // 获取Hashtable中键值对的数量，返回当前存储的元素个数
        System.out.println("Hashtable大小: " + hashtable.size());
        
        // 根据键获取对应的值，如果键不存在则返回null
        System.out.println("姓名: " + hashtable.get("name"));
        // 测试获取不存在的键，会返回null
        System.out.println("测试不存在的键：" + hashtable.get("测试不存在的键"));
        
        // 检查Hashtable中是否包含指定的键，返回boolean值
        System.out.println("是否包含键'age': " + hashtable.containsKey("age"));
        // 检查Hashtable中是否包含指定的值，返回boolean值
        System.out.println("是否包含值'程序员': " + hashtable.containsValue("程序员"));
        
        // 删除指定键的键值对，返回被删除的值，如果键不存在则返回null
        String removedValue = hashtable.remove("job");
        System.out.println("删除的job值: " + removedValue);
        // 打印删除指定键值对后的Hashtable
        System.out.println("删除后的Hashtable: " + hashtable);
        
        // 2. Hashtable与HashMap的区别测试
        System.out.println("\n======= Hashtable与HashMap的区别 =======");
        
        // Hashtable不允许null键和null值
        // 尝试插入null键会抛出NullPointerException
        try {
            hashtable.put(null, "测试null键");
        } catch (NullPointerException e) {
            System.out.println("Hashtable不能存储null键: " + e.getMessage());
        }
        
        // 尝试插入null值也会抛出NullPointerException
        try {
            hashtable.put("nullValueKey", null);
        } catch (NullPointerException e) {
            System.out.println("Hashtable不能存储null值: " + e.getMessage());
        }
        
        // HashMap允许null键和null值
        HashMap<String, String> hashMap = new HashMap<>();
        hashMap.put(null, "null键的值");      // HashMap允许null键
        hashMap.put("nullValueKey", null);    // HashMap允许null值
        System.out.println("HashMap允许null键和null值: " + hashMap);
        
        // 3. Hashtable线程安全性测试
        System.out.println("\n======= Hashtable线程安全性 =======");
        // 虽然Hashtable是线程安全的，但在复合操作中仍需要外部同步
        // 创建一个用于测试线程安全的Hashtable
        Hashtable<String, Integer> syncTable = new Hashtable<>();
        syncTable.put("count", 0);
        
        // 创建多个线程同时修改Hashtable中的值
        // 这里创建5个线程来演示线程安全问题
        Thread[] threads = new Thread[5];
        for (int i = 0; i < threads.length; i++) {
            final int threadNum = i;
            threads[i] = new Thread(() -> {
                // 每个线程执行100次增加操作
                for (int j = 0; j < 100; j++) {
                    // 注意：即使Hashtable是线程安全的，这种"先获取再设置"的操作仍需要外部同步
                    // 这是因为get和put是两个独立的同步操作，中间可能被其他线程打断
                    Integer current = syncTable.get("count");
                    syncTable.put("count", current + 1);
                }
                System.out.println("线程" + threadNum + "执行完毕");
            });
        }
        
        // 启动所有线程
        for (Thread thread : threads) {
            thread.start();
        }
        
        // 等待所有线程执行完毕，确保所有操作都完成
        try {
            for (Thread thread : threads) {
                thread.join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        // 理论上应该是500(5个线程*100次操作)，但由于线程安全问题可能不是
        System.out.println("期望值: 500, 实际值: " + syncTable.get("count"));
        System.out.println("注意：虽然Hashtable是线程安全的，但复合操作仍可能导致竞态条件");
        
        // 4. Hashtable遍历方式
        System.out.println("\n======= Hashtable遍历方式 =======");
        // 创建测试遍历的Hashtable
        Hashtable<String, String> traverseTable = new Hashtable<>();
        traverseTable.put("A", "Apple");
        traverseTable.put("B", "Banana");
        traverseTable.put("C", "Cat");
        traverseTable.put("D", "Dog");
        
        // 使用entrySet遍历键值对
        // entrySet返回Map.Entry对象的Set集合
        System.out.println("方式1 - entrySet遍历:");
        for (Map.Entry<String, String> entry : traverseTable.entrySet()) {
            System.out.println(entry.getKey() + " : " + entry.getValue());
        }
        
        // 使用keySet遍历键
        // keySet返回所有键的Set集合
        System.out.println("\n方式2 - keySet遍历:");
        for (String key : traverseTable.keySet()) {
            System.out.println(key + " : " + traverseTable.get(key));
        }
        
        // 使用elements遍历值 (Hashtable特有的方法)
        // elements返回所有值的Enumeration枚举
        System.out.println("\n方式3 - elements遍历值:");
        for (Enumeration<String> elements = traverseTable.elements(); elements.hasMoreElements();) {
            System.out.println(elements.nextElement());
        }
        
        // 5. Hashtable常用方法测试
        System.out.println("\n======= Hashtable常用方法 =======");
        // 创建测试方法的Hashtable
        Hashtable<String, Integer> methodTable = new Hashtable<>();
        methodTable.put("score1", 85);
        methodTable.put("score2", 92);
        
        // contains方法检查是否包含指定值 (注意：contains方法等价于containsValue)
        // contains方法是遗留方法，建议使用containsValue
        System.out.println("是否包含值85: " + methodTable.contains(85));
        System.out.println("是否包含值85: " + methodTable.containsValue(85));
        // containsKey方法检查是否包含指定键
        System.out.println("是否包含键score1: " + methodTable.containsKey("score1"));
        
        // clone方法创建Hashtable的浅拷贝
        // clone返回Hashtable的一个副本，键值对是相同的引用
        @SuppressWarnings("unchecked")
        Hashtable<String, Integer> clonedTable = (Hashtable<String, Integer>) methodTable.clone();
        System.out.println("克隆的Hashtable: " + clonedTable);
        
        // keys方法获取所有键的枚举 (Hashtable特有的方法)
        // keys返回所有键的Enumeration枚举
        System.out.println("\n所有键:");
        for (Enumeration<String> keys = methodTable.keys(); keys.hasMoreElements();) {
            System.out.println(keys.nextElement());
        }
        
        System.out.println("\nHashtable功能测试完成！");
    }
}